
package JET.Centers.Graphics;

import JET.CenterPanels.CPScene;
import JET.CenterPanels.CPScriptEditor;
import JET.Centers.ControlCenter;
import JET.Centers.Scripts.MyScript;
import JET.Exceptions.JETGraphicsException;
import JET.Frames.*;
import JET.Frames.actionframe.ActionsDialog;
import JET.Frames.keyboardframe.KeyboardDialog;
import JET.Frames.materialsframe.MaterialsDialog;
import JET.Frames.recorderframe.RecorderDialog;
import JET.Frames.toolsoptionspanels.*;
import JET.MouseStuff.MouseMode;
import JET.Scene;
import JET.UtilPanels.UPBodies;
import JET.UtilPanels.UPMass;
import JET.UtilPanels.UPOutput;
import JET.UtilPanels.UPProject;
import JET.UtilPanels.UPRSpring;
import JET.UtilPanels.UPSceneInspector;
import JET.UtilPanels.UPSceneItemsInspector;
import JET.UtilPanels.UPScripts;
import JET.UtilPanels.UPSpring;
import JET.UtilPanels.UPSpringBody;
import JET.UtilPanels.UPTools;
import JET.UtilPanels.UPToolsOptions;
import JET.UtilPanels.UPTorque;
import JET.geom.Vec2d;
import JET.mygui.CenterTabsPane;
import JET.mygui.CenterTabsPane.CenterTabsPaneTab;
import JET.mygui.MultiSplitPane;
import JET.mygui.MyGuiHelper;
import JET.mygui.MyGuiManager;
import JET.mygui.TabsPane;
import JET.mygui.UtilTabsPane.UtilTabsPaneTab;
import XMLStuff.XMLHelper;
import java.awt.Component;
import java.awt.Frame;
import java.awt.event.MouseEvent;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import javax.xml.parsers.*;
import org.w3c.dom.*;

/**
 *
 * @author raos
 */
public class GUICenter {

    /** All available dialogs in jet gui */
    public enum UtilTabs {
        Mass,
        Spring,
        RSpring,
        Tools,
        Bodies,
        ToolsOptions,
        SpringBody,
        Torque,
        Materials,
        Scripts,
        Output,
        SceneInspector,
        SceneItemsInspector,
        Project
    }

    static final ControlCenter CC = ControlCenter.getInstance();
    
    // zmienne do zarzadzania okienkami aplikacji
    public MaterialsDialog      materialsDialog;
    public ActionsDialog        actionsDialog;
    public KeyboardDialog       keyboadDialog;
    public RecorderDialog       recorderDialog;

    // Hashtable to hold all gui panels
    protected HashMap<UtilTabs, UtilTabsPaneTab> utilTabs;
    protected HashMap<MyScript, CenterTabsPaneTab> scriptTabs;
    protected HashMap<String, CenterTabsPaneTab> sceneTabs;

    protected String currentlSceneName = "";

    // Cached root frame reference
    private Frame rootFrame;
    // Look for root frame or return cached reference
    private boolean findRootFrame = true;
    // Ceched main frame reference
    private MainFrame mainFrame;
    
    public GUICenter() {
        initUtilTabs();
        initScriptTabs();
        initSceneTabs();
    }

    public final void initUtilTabs() {
        utilTabs = new HashMap<UtilTabs, UtilTabsPaneTab>();
        utilTabs.put(UtilTabs.Mass, new UPMass());
        utilTabs.put(UtilTabs.Spring, new UPSpring());
        utilTabs.put(UtilTabs.RSpring, new UPRSpring());
        utilTabs.put(UtilTabs.Tools, new UPTools());
        utilTabs.put(UtilTabs.Bodies, new UPBodies());
        utilTabs.put(UtilTabs.ToolsOptions, new UPToolsOptions());
        utilTabs.put(UtilTabs.SpringBody, new UPSpringBody());
        utilTabs.put(UtilTabs.Torque, new UPTorque());
        utilTabs.put(UtilTabs.Output, new UPOutput());
        utilTabs.put(UtilTabs.SceneInspector, new UPSceneInspector());
        utilTabs.put(UtilTabs.SceneItemsInspector, new UPSceneItemsInspector());
        utilTabs.put(UtilTabs.Project, new UPProject());
    }

    public final void initScriptTabs() {
        scriptTabs = new HashMap<MyScript, CenterTabsPaneTab>();
    }

    public final void initSceneTabs() {
        sceneTabs = new HashMap<String, CenterTabsPaneTab>();
    }

    /**
     * Removes and adds graphic panel container into centerTabsPane - so that
     * focus listeners are properly added to all descendants of graphicPanelContainer.
     */
    public void reAddGPContainer(JPanel gPContainer) {
        CenterTabsPane ctp = (CenterTabsPane) gPContainer.getParent();
        if (ctp!=null) {
            int index = ctp.getTabIndex(gPContainer);
            if (index!=-1) {
                ctp.remove(gPContainer);
                ctp.add(gPContainer, index);
            }
        }
    }

    public void setRootFrame(Frame frame){
        rootFrame = frame;
    }

    /**
     * Finds root frame of gui hierarchy.
     * In Desktop mode it will be JFrame (MainFrame swing class),
     * in Applet mode it will be Frame (browser - awt frame).
     *
     * @return Root frame of an application.
     */
    public Frame getRootFrame() {
        /*Collection<CenterTabsPaneTab> sceneTabsCol = getSceneGraphicPanelsSync();
        synchronized(sceneTabsCol) {
            for(CenterTabsPaneTab sceneTabIter : sceneTabsCol) {
                CPScene sceneTab = (CPScene) sceneTabIter;

                if (rootFrame==null || findRootFrame) {
                    Component parent = (Component)sceneTab.getGraphicPanel();
                    while(!(parent instanceof Frame))
                        parent = parent.getParent();

                    rootFrame = (Frame) parent;
                    findRootFrame = false;
                    mainFrame = null;
                }
                return rootFrame;
            }
        }

        return null;*/
        
        return rootFrame;
    }

    /**
     * If findRootFrame is set to true, next call to getRootFrame will actually
     * iterate through graphic panel parents to find actual root frame and renew
     * GUIC root frame reference.
     *
     * @param find
     */
    public void setFindRootFrame(boolean find) {
        findRootFrame = find;
    }

    /**
     * If JET run mode is Desktop - this method finds and returns MainFrame instance.
     * Main Frame differs from RootFrame in the gui functinality of menu bars,
     * popup menus and some labels that display some JET info (like mouse positions,
     * actualm ouse task, mde, sub mode and stuff like that.
     *
     * If JET runs in other mode than Desktop, or generally not in a mode where MainFrame
     * contains more JET gui stuff than just content pane with raphic panel,
     * getMainFrame() will return null.
     *
     * @return Main Frame instance of application (or null in run mode is diffrent than Desktop).
     */
    public MainFrame getMainFrame() {
        if (mainFrame==null) {
            Frame frame = getRootFrame();
            if (frame instanceof MainFrame)
                mainFrame = (MainFrame)frame;
        }
        return mainFrame;
    }
    
////////////////////////////////////////////////////////////////////////////////
//                        G U I   U T I L   T A B S                           //
////////////////////////////////////////////////////////////////////////////////

    public UtilTabsPaneTab getUtilTab(UtilTabs tab) {
        UtilTabsPaneTab ret = utilTabs.get(tab);
        return ret;
    }

////////////////////////////////////////////////////////////////////////////////
//                       G U I   S C E N E   T A B S                          //
////////////////////////////////////////////////////////////////////////////////

    public CenterTabsPaneTab getSceneTab(String sceneName) {
        CenterTabsPaneTab ret = (CenterTabsPaneTab) sceneTabs.get(sceneName);
        return ret;
    }

    public void renameSceneTab(String sceneName, String newName) {
        CPScene sceneTab = (CPScene) sceneTabs.get(sceneName);

        if (sceneTab != null) {
            System.out.println("0: '"+sceneName+"'");
            System.out.println("1: '"+newName+"'");
            System.out.println("1: '"+currentlSceneName+"'");

            sceneTabs.remove(sceneName);
            sceneTabs.put(newName, sceneTab);
            sceneTab.getAccessibleContext().setAccessibleName(newName);
            sceneTab.setSceneName(newName);

            TabsPane tp = MyGuiHelper.getTabsPaneOf(sceneTab, MyGuiManager.getInstance().getRootMSP());
            tp.updateUI();

            if (currentlSceneName.equals(sceneName))
                setCurrentScene(newName);
        }
    }

    public Collection<CenterTabsPaneTab> getSceneGraphicPanelsSync() {
        return Collections.synchronizedCollection(sceneTabs.values());
    }

    public void openSceneTab(String sceneName) {

        if (sceneTabs.get(sceneName) == null) {
            Scene scene = CC.getScene(sceneName);
            CPScene sceneTab = new CPScene();
            sceneTab.getAccessibleContext().setAccessibleName("Scene["+sceneName+"]");
            sceneTab.setName("Scene["+sceneName+"]");
            sceneTab.setSceneName(sceneName);

            sceneTabs.put(sceneName, sceneTab);

            try {
                scene.GC.setGraphicMode(GraphicMode.Swing);
                scene.GC.getGraphicPanel().setViewScale(2.99);
                scene.GC.getGraphicPanel().centerViewToVec(new Vec2d(100, 100));
            } catch (JETGraphicsException ex) {
                Logger.getLogger(GUICenter.class.getName()).log(Level.SEVERE, null, ex);
            }

            currentlSceneName = sceneName;
        }

        CPScene sceneTab = (CPScene) sceneTabs.get(sceneName);
        if (!sceneTab.isTabOpened()) {
            sceneTab.setTabOpened(true);
            
            CenterTabsPane ctp = (CenterTabsPane) MyGuiHelper.listGUIComponentsByClass(MyGuiManager.getInstance().getRootMSP(), CenterTabsPane.class).get(0);
            ctp.add(sceneTab);
        }
        else {
            TabsPane tp = MyGuiHelper.getTabsPaneOf(sceneTab, MyGuiManager.getInstance().getRootMSP());
            tp.displayTab(sceneTab);
            sceneTab.requestFocus();
        }
    }

    public void closeSceneTab(String sceneName) {
        CPScene sceneTab = (CPScene) sceneTabs.get(sceneName);
        if (sceneTab!=null && sceneTab.isTabOpened()) {
            TabsPane tp = MyGuiHelper.getTabsPaneOf(sceneTab, MyGuiManager.getInstance().getRootMSP());
            tp.remove(sceneTab);
            ArrayList<Component> tabsPaneList = MyGuiHelper.listGUIComponentsByClass(MyGuiManager.getInstance().getRootMSP(), CenterTabsPane.class);
            if (tabsPaneList.size()>1)
                MyGuiHelper.clearTabsPaneAfterLastTabRemoval(tp);
            sceneTab.setTabOpened(false);
        }
    }

    public void closeAllSceneTabs() {
        for(CenterTabsPaneTab tab : sceneTabs.values())
            closeSceneTab(((CPScene)tab).getSceneName());
    }

    public void removeSceneTab(String sceneName) {
       sceneTabs.remove(sceneName);
    }

    public void removeAllSceneTabs() {
        for(String sceneName : sceneTabs.keySet())
            removeSceneTab(sceneName);
    }

    public void updateView(String sceneName) {
        CPScene sceneTab = (CPScene)sceneTabs.get(sceneName);
        ((Component)sceneTab.getGraphicPanel()).repaint();
    }
    
    public void repaintAllSceneTabs() {
        Collection<CenterTabsPaneTab> sceneTabsCol = getSceneGraphicPanelsSync();
        synchronized(sceneTabsCol) {
            for(CenterTabsPaneTab sceneTabIter : sceneTabsCol) {
                CPScene sceneTab = (CPScene) sceneTabIter;
                updateView(sceneTab.getSceneName());
            }
        }
    }

    public String getCurrentSceneName() {
        return currentlSceneName;
    }
    
    public void setCurrentScene(String sceneName) {
        Scene scene = CC.getScene(sceneName);

        if (scene!=null) {
            currentlSceneName = sceneName;

            // Util Panels
            ((UPRSpring)getUtilTab(UtilTabs.RSpring)).setScene(scene);
            ((UPSceneInspector)getUtilTab(UtilTabs.SceneInspector)).setScene(scene);
            ((UPSceneItemsInspector)getUtilTab(UtilTabs.SceneItemsInspector)).setScene(scene);
            ((UPSpring)getUtilTab(UtilTabs.Spring)).setScene(scene);
            ((UPSpringBody)getUtilTab(UtilTabs.SpringBody)).setScene(scene);
            ((UPTools)getUtilTab(UtilTabs.Tools)).setScene(scene);
            ((UPTorque)getUtilTab(UtilTabs.Torque)).setScene(scene);

            // Dialogs
            materialsDialog.setScene(scene);

            // MainFrame
            MainFrame frame = getMainFrame();
            if (frame!=null) {
                frame.updateSceneMenu(scene);
            }
        }
    }

////////////////////////////////////////////////////////////////////////////////
//                               D I A L O G S                                //
////////////////////////////////////////////////////////////////////////////////

    public void initDialogs() {
        materialsDialog = new MaterialsDialog(getRootFrame(), false);
        materialsDialog.setLocationRelativeTo(null);

        actionsDialog = new ActionsDialog(getRootFrame(), false);
        actionsDialog.setLocationRelativeTo(null);

        keyboadDialog = new KeyboardDialog(getRootFrame(), false);
        keyboadDialog.setLocationRelativeTo(null);

        recorderDialog = new RecorderDialog(getRootFrame(), false);
        recorderDialog.setLocationRelativeTo(null);
    }

////////////////////////////////////////////////////////////////////////////////
//                               B U T T O N S                                //
////////////////////////////////////////////////////////////////////////////////

    public void setRefreshButtonEnabled(boolean value) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setRefreshButtonEnabled(value);
            case Applet  : break;
        }
    }

    public void setFollowCameraButtonSelected(boolean value) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setFollowCameraButtonSelected(value);
            case Applet  : break;
        }
    }

    public void setDrawActionFrameButtonSelected(boolean value) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setDrawActionFrameButtonSelected(value);
            case Applet  : break;
        }
    }

    public void setRecordButtonEnabled(boolean value) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setRecordButtonEnabled(value);
            case Applet  : break;
        }
    }

    public void setMouseScreenPos(MouseEvent evt) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setMouseScreenPos(evt);
            case Applet  :  break;
        }
    }
    public void setMouseEnginePos(MouseEvent evt) {
        switch(CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setMouseEnginePos(evt);
            case Applet  :  break;
        }
    }
    public void showPopupSpringBody(MouseEvent evt) {
        switch (CC.runMode) {
            case Desktop : CC.GUIC.getMainFrame().showPopupSpringBody(evt); break;
            case Applet  : break;
        }
    }
    public void showPopupScene(MouseEvent evt) {
        switch (CC.runMode) {
            case Desktop : CC.GUIC.getMainFrame().showPopupScene(evt); break;
            case Applet  : break;
        }
    }
    public void setMouseTaskLabel(String text) {
        switch(CC.runMode) {
            case Desktop : CC.GUIC.getMainFrame().setMouseTaskLabel(text); break;
            case Applet  : break;
        }
    }
    public void setViewScaleLabel(double scale) {
        switch(CC.runMode) {
            case Desktop : CC.GUIC.getMainFrame().setViewScaleLabel(scale); break;
            case Applet  : break;
        }
    }
    public void setTitle(String text) {
        switch(CC.runMode) {
            case Desktop : CC.GUIC.getMainFrame().setTitle(text); break;
            case Applet  : break;
        }
    }

////////////////////////////////////////////////////////////////////////////////
//                                L A B E L S                                 //
////////////////////////////////////////////////////////////////////////////////

    public void setMouseModeLabel(String text) {
        switch (CC.runMode) {
            case Desktop :  CC.GUIC.getMainFrame().setMouseModeLabel(text);  break;
            case Applet  :  // DoNothing
                break;
        }
    }

////////////////////////////////////////////////////////////////////////////////
//                         T O O L S   O P T I O N S                          //
////////////////////////////////////////////////////////////////////////////////

    public void setToolsOptionsPanelMode(MouseMode mouseMode) {
        UPToolsOptions upToolsOptions = (UPToolsOptions) getUtilTab(UtilTabs.ToolsOptions);
        switch (CC.runMode) {
            case Desktop :
            case Applet :
                switch(mouseMode) {
                    case ADD_SINGLE_MASS :
                        upToolsOptions.removeAll();
                        upToolsOptions.add( new AddMassToolOptionsPanel() );
                        break;
                    case MOVE_MASSES :
                        upToolsOptions.removeAll();
                        upToolsOptions.add( new MoveToolOptionsPanel() );
                        break;
                    case CREATE_SQUARENET:
                        upToolsOptions.removeAll();
                        upToolsOptions.add( new CreateRectBodyToolOptionPanel() );
                        break;
                    default :
                        upToolsOptions.removeAll();
                        upToolsOptions.add(new JLabel("No tools selected or no options for this tool."));
                        break;
                }
                upToolsOptions.updateUI();
                break;
        }
        
    }

////////////////////////////////////////////////////////////////////////////////
//                                O U T P U T                                 //
////////////////////////////////////////////////////////////////////////////////

    /**
     * Appends result of toString() obj method call.
     * @param obj
     */
    public synchronized void printToOutput(Object obj) {
        if (obj==null)
            return;

        switch (CC.runMode) {
            case Applet :
            case Desktop :
                UPOutput upOutput = (UPOutput) utilTabs.get(UtilTabs.Output);
                upOutput.print(obj.toString());
                break;
        }
    }

    /**
     * Appends result of toString() obj method call and begins new line.
     * @param obj
     */
    public synchronized void printlnToOutput(Object obj) {
        if (obj==null)
            return;

        switch (CC.runMode) {
            case Applet :
            case Desktop :
                UPOutput upOutput = (UPOutput) utilTabs.get(UtilTabs.Output);
                upOutput.println(obj.toString());
                break;
        }
    }


    /**
     * Clears code editor output.
     */
    public synchronized void clearOutput() {
        switch (CC.runMode) {
            case Applet :
            case Desktop : SwingUtilities.invokeLater( new Thread( new Runnable(){
                    public void run() {
                        UPOutput upOutput = (UPOutput) utilTabs.get(UtilTabs.Output);
                        upOutput.clear();
                    }
                }));
                break;
        }
    }

////////////////////////////////////////////////////////////////////////////////
//                          B U I L D I N G   G U I                           //
////////////////////////////////////////////////////////////////////////////////

    /**
     *
     * @param path Path to XML file with dockable gui structure
     * @return
     */
    public MultiSplitPane builGuiSctructure(String path, ControlCenter CC) {
        InputStream is = null;
        try {
            is = new FileInputStream(path);
            return builGuiSctructure(is, CC);
        } catch(IOException ioe) {
            return null;
        } finally {
            if (is!=null) try {is.close();}catch(IOException ex) {ex.printStackTrace();}
        }
    }

    /**
     *
     * @param path Path to XML file with dockable gui structure
     * @return
     */
    public MultiSplitPane builGuiSctructure(InputStream guiXmlIs, ControlCenter CC) {
        Document doc = null;
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.parse(guiXmlIs);
        } catch (Exception e) {e.printStackTrace();}

        // msp root element
        Element root = doc.getDocumentElement();

        return XMLHelper.readMultiSplitPane(root, CC);
    }


////////////////////////////////////////////////////////////////////////////////
//                              S C R I P T S                                 //
////////////////////////////////////////////////////////////////////////////////

    public void registerScript(MyScript script, CenterTabsPaneTab tab) {
        scriptTabs.put(script, tab);
    }
    
    public void unregisterScript(MyScript script) {
        CPScriptEditor editor = (CPScriptEditor) scriptTabs.get(script);
        CenterTabsPane ctp = (CenterTabsPane) MyGuiHelper.getTabsPaneOf(editor, MyGuiManager.getInstance().getRootMSP());
        ctp.remove(editor);
        
        scriptTabs.remove(script);
    }


    public void openScriptTab(MyScript script) {
        if (!script.isEdited()) {
            script.setEdited(true);
            CPScriptEditor editor = new CPScriptEditor();
            editor.setScript(script);
            registerScript(script, (CenterTabsPaneTab) editor);
            
            CenterTabsPane ctp = (CenterTabsPane) MyGuiHelper.listGUIComponentsByClass(MyGuiManager.getInstance().getRootMSP(), CenterTabsPane.class).get(0);
            ctp.add(editor);
        }
        else {
            CPScriptEditor editor = (CPScriptEditor) scriptTabs.get(script);
            TabsPane tp = MyGuiHelper.getTabsPaneOf(editor, MyGuiManager.getInstance().getRootMSP());
            tp.displayTab(editor);
            editor.requestFocus();
        }
    }

    public void closeScriptTab(MyScript script) {
        if (script.isEdited()) {
            script.setEdited(false);
            unregisterScript(script);
        }
    }

    public void closeAllScriptTabs() {
        for(Scene scene : CC.scenes.values())
            for(MyScript s : scene.SC.getScripts())
                closeScriptTab(s);
    }

    /**
     * Iterates through all opened script editor tabs and saves its content in MyScript objects.
     */
    public void saveAllScripts() {
        for(CenterTabsPaneTab tab: scriptTabs.values())
            ((CPScriptEditor) tab).saveScript();
    }
}